
# include "dfxImage.h"
# include "dfxLog.h"
# include "FreeImage/FreeImage.h"

# pragma comment (lib, "FreeImage/FreeImage.lib")

namespace DFX
{
    struct RGB
    {
        UInt8 R, G, B;
    };

    struct RGBA
    {
        UInt8 R, G, B, A;
    };

    void Image::ConvertToBGR()
    {
        if (!mData)
            return;

        if (mFormat == IMAGE_FORMAT_BGR8)
        {
            for (UInt32 y = 0; y < mHeight; y++)
            {
                for (UInt32 x = 0; x < mWidth; x++)
                {
                    RGB *c = (RGB*)&mData[(y * mWidth + x) * 3];
                    std::swap(c->R, c->B);
                }
            }
        }
        else if (mFormat == IMAGE_FORMAT_BGRA8)
        {
            for (UInt32 y = 0; y < mHeight; y++)
            {
                for (UInt32 x = 0; x < mWidth; x++)
                {
                    RGBA *c = (RGBA*)&mData[(y * mWidth + x) * 4];
                    std::swap(c->R, c->B);
                }
            }
        }
    }

    void Image::SetPixel(UInt32 x, UInt32 y, const Color4 &c)
    {
        if (x < 0 || x >= mWidth ||
            y < 0 || y >= mHeight)
        {
            DFX_WARNING( "Invalid co-ordinates for setpixel (%ld, %ld)", x, y);
            return;
        }

        if (mFormat == IMAGE_FORMAT_BGR8)
        {
            RGB *clr = (RGB*)&mData[(y * mWidth + x) * 3];

            clr->R = UInt8(c.R * 255.0f);
            clr->G = UInt8(c.G * 255.0f);
            clr->B = UInt8(c.B * 255.0f);
        }
        else if (mFormat == IMAGE_FORMAT_BGRA8)
        {
            RGBA *clr = (RGBA*)&mData[(y * mWidth + x) * 4];

            clr->R = UInt8(c.R * 255.0f);
            clr->G = UInt8(c.G * 255.0f);
            clr->B = UInt8(c.B * 255.0f);
            clr->A = UInt8(c.A * 255.0f);
        }
    }

    void Image::Clear()
    {
        if (!mData)
            return;

        memset(mData, 0, mDataSize);
    }

    void Image::Rescale(UInt32 width, UInt32 height)
    {
        FIBITMAP *fiBitmap = FreeImage_ConvertFromRawBits((BYTE*)mData, mWidth, mHeight,
            mWidth * mHeight * (mBitsPerPixel / 8), mBitsPerPixel / 8, 2, 1, 0, FALSE);

        FIBITMAP *fiConvBitmap = FreeImage_Rescale(fiBitmap, width, height, FILTER_BSPLINE);

        mBitsPerPixel = FreeImage_GetBPP(fiConvBitmap);
        mWidth = FreeImage_GetWidth(fiConvBitmap);
        mHeight = FreeImage_GetHeight(fiConvBitmap);
        mFormat = (mBitsPerPixel == 24) ? IMAGE_FORMAT_BGR8 : IMAGE_FORMAT_BGRA8;

        SAFE_DELETE(mData);

        mDataSize = mWidth * mHeight * (mBitsPerPixel / 8);
        mData = new UInt8[mDataSize];

        memcpy(mData, FreeImage_GetBits(fiConvBitmap), mDataSize);

        FreeImage_Unload(fiConvBitmap);
    }

    DFXRESULT Image::Load(InputStream *stream, bool flipV)
    {
        FIMEMORY *fiMem = FreeImage_OpenMemory(const_cast<BYTE*>(stream->MapStreamFromCurrentPosition()),
            static_cast<DWORD>(stream->GetSize()));

        if (!fiMem)
        {
            DFX_ERROR( "Unable to open image %s", mName.c_str());
            return DFX_LOADFAILED;
        }

        FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(fiMem);
        FIBITMAP *fiBitmap = FreeImage_LoadFromMemory(fif, fiMem);

        if (!fiBitmap)
        {
            DFX_ERROR( "Unable to load image %s", mName.c_str());
            return DFX_LOADFAILED;
        }

        FreeImage_CloseMemory(fiMem);

        FIBITMAP *fiConvBitmap = (FreeImage_IsTransparent(fiBitmap) == TRUE) ?
            FreeImage_ConvertTo32Bits(fiBitmap) : FreeImage_ConvertTo24Bits(fiBitmap);

        FreeImage_Unload(fiBitmap);

        if (flipV)
            FreeImage_FlipVertical(fiConvBitmap);

        mBitsPerPixel = FreeImage_GetBPP(fiConvBitmap);
        mWidth = FreeImage_GetWidth(fiConvBitmap);
        mHeight = FreeImage_GetHeight(fiConvBitmap);
        mFormat = (mBitsPerPixel == 24) ? IMAGE_FORMAT_BGR8 : IMAGE_FORMAT_BGRA8;

        mDataSize = mWidth * mHeight * (mBitsPerPixel / 8);
        mData = new UInt8[mDataSize];

        memcpy(mData, FreeImage_GetBits(fiConvBitmap), mDataSize);

        FreeImage_Unload(fiConvBitmap); 

        return DFX_SUCCESS;
    }

    DFXRESULT Image::Destroy()
    {
        mDataSize = 0;
        SAFE_DELETE(mData);

        return DFX_SUCCESS;
    }
}